package com.zyh.em.evaluate;

import com.zyh.em.entity.EvaluateData;
import com.zyh.em.entity.EvaluateReport;
import com.zyh.em.entity.HistoryData;

import java.util.List;
import java.util.stream.Collectors;

/**
 * 输入特征评估
 */
public class InputFeatureEvaluate  extends AbstractEvaluate{
    @Override
    public void evaluate(EvaluateData evaluateData, HistoryData historyData, EvaluateReport evaluateReport, EvaluateChain evaluateChain) {

        //1.计算
        double[] inputFeature = evaluateData.getInputFeatures();
        List<double[]> historyInputFeatures = historyData.getInputFeatures();

        //如果历史数据为空，则直接返回无风险
        if(historyInputFeatures == null || historyInputFeatures.size() == 0){
            evaluateReport.setRiskFactor("inputFeatures",false);
        }else {
            boolean isRisk = doEval(inputFeature, historyInputFeatures);
            evaluateReport.setRiskFactor("inputFeatures", isRisk);
        }

        //2.交给责任链管理
        evaluateChain.doEvaluate(evaluateData, historyData, evaluateReport);
    }

    /**
     * 评估
     * @param inputFeatures 评估数据
     这是神秘
     * @param list 历次成功登录数据
     * @return 是否有风险
     */
    private boolean doEval(double[] inputFeatures, List<double[]> list) {

        //计算球心：list中所有元素的算术平均
        double[] sum=new double[list.get(0).length];//存储每一个输入框总的时间
        for (double[] e : list) {
            for (int i = 0; i < e.length; i++) {
                sum[i]+=e[i];
            }
        }
        double[] qiuXin=new double[sum.length];
        for (int i = 0; i < sum.length; i++) {
            qiuXin[i]=sum[i]/list.size();
        }


        //1.计算评估数据到球心点的欧式距离
        double distance = calcEuclideanDistance(inputFeatures, qiuXin);

        //2.计算历次成功登录的数据到球心的欧式距离，然后按照升序排列，取三分之二位置处的值作为阈值
        List<Double> l = list
                .stream()
                .map(e -> calcEuclideanDistance(e, qiuXin))
                .sorted()
                .collect(Collectors.toList());
        double threshold = l.get(l.size() * 2 / 3);

        return distance>threshold;
    }

    /**
     * 计算欧式距离
     * @param x 点1
     * @param y 点2
     * @return 两点的欧式距离
     */
    private double calcEuclideanDistance(double[] x,double[]y){
        double sum=0;
        for (int i = 0; i < x.length; i++) {
            sum+=Math.pow(x[i]-y[i],2);
        }
        return Math.sqrt(sum);
    }
}